package org.btallarm.gui;
import org.btallarm.R.id;
import org.btallarm.R.layout;
import org.btallarm.R.menu;
import org.btallarm.R.string;
import org.btallarm.*;
import org.btallarm.bluetooth.BluetoothMessages;
import org.btallarm.bluetooth.BluetoothService;
import org.btallarm.bluetooth.DeviceListActivity;
import org.btallarm.json.DataConverter;
import org.btallarm.json.NotAnnotationDeclaredException;
import org.btallarm.math.DelayLine;
import org.btallarm.model.Command;
import org.btallarm.model.Envelop;
import org.btallarm.model.Note;
import org.btallarm.model.Threshold;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;


import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ClientActivity extends Activity {
	    // Debugging
	    private static final String TAG = "Bluetooth";
	    private static final boolean D = true;
		
	    // Layout Views
	    private TextView mTitle;
	    private ListView mConversationView;
	    private EditText mOutEditText;
	    private Button mSendButton;

	    // Name of the connected device
	    private String mConnectedDeviceName = null;
	    // Array adapter for the conversation thread
	    private ArrayAdapter<String> mConversationArrayAdapter;
	    // String buffer for outgoing messages
	    private StringBuffer mOutStringBuffer;
	    // Local Bluetooth adapter
	    private BluetoothAdapter mBluetoothAdapter = null;
	    // Member object for the chat services
	    private BluetoothService mService = null;

	    private SensorManager mSensorManager;
	    private CustomView mCustomView;
	    
	        
	    public void onCreate(Bundle savedInstanceState) {
	        super.onCreate(savedInstanceState);
	        if(D) Log.e(TAG, "+++ ON CREATE +++");
	        
	        // Set up the window layout
	        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
	        setContentView(R.layout.client);
	        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);

	        // Set up the custom title
	        mTitle = (TextView) findViewById(R.id.title_left_text);
	        mTitle.setText(R.string.app_name);
	        mTitle = (TextView) findViewById(R.id.title_right_text);

	        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
	        mCustomView= (CustomView) findViewById(R.id.content);
	        
	        // Get local Bluetooth adapter
	        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

	        // If the adapter is null, then Bluetooth is not supported
	        if (mBluetoothAdapter == null) {
	            Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
	            finish();
	            return;
	    	}
	        
	    }

	    
	    @Override
	    public void onStart() {
	        super.onStart();
	        if(D) Log.e(TAG, "++ ON START ++");
	        
	        // If BT is not on, request that it be enabled.
	        // setupChat() will then be called during onActivityResult
	        if (!mBluetoothAdapter.isEnabled()) {
	            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
	            startActivityForResult(enableIntent, BluetoothMessages.REQUEST_ENABLE_BT);
	        // Otherwise, setup the chat session
	        } else {
	            if (mService == null) setupChat();
	        }
	    }

	    @Override
	    public synchronized void onResume() {
	        super.onResume();
	        if(D) Log.e(TAG, "+ ON RESUME +");
	        
	        // Performing this check in onResume() covers the case in which BT was
	        // not enabled during onStart(), so we were paused to enable it...
	        // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
	        if (mService != null) {
	            // Only if the state is STATE_NONE, do we know that we haven't started already
	            if (mService.getState() == BluetoothService.STATE_NONE) {
	              // Start the Bluetooth chat services
	              mService.start();
	            }
	        }
	    }
	    private void setupChat() {
	        Log.d(TAG, "setupChat()");

	        // Initialize the array adapter for the conversation thread
	        mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
	        mConversationView = (ListView) findViewById(R.id.in);
	        mConversationView.setAdapter(mConversationArrayAdapter);

	        // Initialize the compose field with a listener for the return key
	        /*mOutEditText = (EditText) findViewById(R.id.edit_text_out);
	        mOutEditText.setOnEditorActionListener(mWriteListener);

	        // Initialize the send button with a listener that for click events
	        mSendButton = (Button) findViewById(R.id.button_send);
	        mSendButton.setOnClickListener(new OnClickListener() {
	            public void onClick(View v) {
	                // Send a message using content of the edit text widget
	                TextView view = (TextView) findViewById(R.id.edit_text_out);
	                String message = view.getText().toString();
	                sendMessage(message);
	            }
	        });*/

	        // Initialize the BluetoothService to perform bluetooth connections
	        mService = new BluetoothService(this, mHandler);

	        // Initialize the buffer for outgoing messages
	        mOutStringBuffer = new StringBuffer("");
	    }
	    @Override
	    public synchronized void onPause() {
	        super.onPause();
	        if(D) Log.e(TAG, "- ON PAUSE -");
	    }

	    @Override
	    public void onStop() {
	        super.onStop();
	        if(D) Log.e(TAG, "-- ON STOP --");
	        
	    }

	    @Override
	    public void onDestroy() {
	        super.onDestroy();
	        // Stop the Bluetooth chat services
	        if (mService != null) mService.stop();
	        if(D) Log.e(TAG, "--- ON DESTROY ---");
	    }

	    private void ensureDiscoverable() {
	        if(D) Log.d(TAG, "ensure discoverable");
	        if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
	            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
	            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
	            startActivity(discoverableIntent);
	        }
	    }

	    /**
	     * Sends a message.
	     * @param message  A string of text to send.
	     */
	    private void sendMessage(String message) {
	        // Check that we're actually connected before trying anything
	        if (mService.getState() != BluetoothService.STATE_CONNECTED) {
	            Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
	            return;
	        }

	        // Check that there's actually something to send
	        if (message.length() > 0) {
	            // Get the message bytes and tell the BluetoothChatService to write
	            byte[] send = message.getBytes();
	            mService.write(send);

	            // Reset out string buffer to zero and clear the edit text field
	            mOutStringBuffer.setLength(0);
	            mOutEditText.setText(mOutStringBuffer);
	        }
	    }

	    // The action listener for the EditText widget, to listen for the return key
	   /* private TextView.OnEditorActionListener mWriteListener = new TextView.OnEditorActionListener() {
	        public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
	            // If the action is a key-up event on the return key, send the message
	            if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
	                String message = view.getText().toString();
	                sendMessage(message);
	            }
	            if(D) Log.i(TAG, "END onEditorAction");
	            return true;
	        }
	    };*/

	    // The Handler that gets information back from the BluetoothChatService
	    private final Handler mHandler = new Handler() {
	        @Override
	        public void handleMessage(Message msg) {
	            switch (msg.what) {
	            case BluetoothMessages.MESSAGE_STATE_CHANGE:
	                if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
	                switch (msg.arg1) {
	                case BluetoothService.STATE_CONNECTED:
	                    mTitle.setText(R.string.title_connected_to);
	                    mTitle.append(mConnectedDeviceName);
	                    mConversationArrayAdapter.clear();
	                    break;
	                case BluetoothService.STATE_CONNECTING:
	                    mTitle.setText(R.string.title_connecting);
	                    break;
	                case BluetoothService.STATE_LISTEN:
	                case BluetoothService.STATE_NONE:
	                    mTitle.setText(R.string.title_not_connected);
	                    break;
	                }
	                break;
	            case BluetoothMessages.MESSAGE_WRITE:
	                byte[] writeBuf = (byte[]) msg.obj;
	                // construct a string from the buffer
	                String writeMessage = new String(writeBuf);
	                mConversationArrayAdapter.add("Me:  " + writeMessage);
	                break;
	            case BluetoothMessages.MESSAGE_READ:
	                byte[] readBuf = (byte[]) msg.obj;
	                // construct a string from the valid bytes in the buffer
	                String readMessage = new String(readBuf, 0, msg.arg1);
	                mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);
	                client_recv(readMessage);
	                break;
	            case BluetoothMessages.MESSAGE_DEVICE_NAME:
	                // save the connected device's name
	                mConnectedDeviceName = msg.getData().getString(BluetoothMessages.DEVICE_NAME);
	                Toast.makeText(getApplicationContext(), "Connected to "
	                               + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
	                break;
	            case BluetoothMessages.MESSAGE_TOAST:
	                Toast.makeText(getApplicationContext(), msg.getData().getString(BluetoothMessages.TOAST),
	                               Toast.LENGTH_SHORT).show();
	                break;
	            }
	        }
	    };
	   
	    void client_recv(String message){
	    	
			try {
				JSONObject obj = new JSONObject(new JSONTokener(message));
				Envelop env = new Envelop();
			    env.fromJSONObject(obj);
			    
			    if (env.getId() == Envelop.MESSAGE_COMMAND){
			    
			    	Command cmd= new Command ();
			    	cmd=(Command) env.getObject();
			    
			    	if (cmd.getAction().equals("start")){
			    		sensors_onStart();
			    		mConversationArrayAdapter.add("Turn on sensors");
			    	}else if (cmd.getAction().equals("stop")){
			    		sensors_onStop();
			    		mConversationArrayAdapter.add("Turn off sensors");
			    	}else if ( cmd.getAction().equals("note") ){
			    		Envelop env1 = new Envelop();
			    		env.setId(Envelop.MESSAGE_NOTE);       
			    		env.setObject(mCustomView.getNote());
			    		JSONObject json= env.toJSONObject();
			    		String string = json.toString();
			    		sendMessage(string);
			    		mConversationArrayAdapter.add("Send note information");
			    	}else if ( cmd.getAction().equals("state") ){
			    		Envelop env1 = new Envelop();
			    		env.setId(Envelop.MESSAGE_STATE);       
			    		env.setObject(mCustomView.getState());
			    		JSONObject json= env.toJSONObject();
			    		String string = json.toString();
			    		sendMessage(string);
			    		mConversationArrayAdapter.add("Send state information");
			    	}
			    } else if (env.getId() == Envelop.MESSAGE_NOTE){
			    	Note note = new Note();
			    	note = (Note) env.getObject();
			    	mCustomView.setNote(note);
			    } else if (env.getId() == Envelop.MESSAGE_THRESHOLD){
			    	Threshold threshold = new Threshold();
			    	threshold = (Threshold) env.getObject();
			    	mCustomView.setThreshold(threshold);
			    }			    
			    	
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		   
	    	
	    }
	    
	    private void sensors_onStop(){
	    	mSensorManager.unregisterListener(mCustomView);
	    }
	    private void sensors_onStart(){
	    	   mSensorManager.registerListener(mCustomView,
		                mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
		                SensorManager.SENSOR_DELAY_FASTEST);
		        mSensorManager.registerListener(mCustomView,
		                mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
		                SensorManager.SENSOR_DELAY_FASTEST);
		        mSensorManager.registerListener(mCustomView, 
		                mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
		                SensorManager.SENSOR_DELAY_FASTEST);
	    }

	    

	    public void onActivityResult(int requestCode, int resultCode, Intent data) {
	        if(D) Log.d(TAG, "onActivityResult " + resultCode);
	        switch (requestCode) {
	        case BluetoothMessages.REQUEST_CONNECT_DEVICE_SECURE:
	            // When DeviceListActivity returns with a device to connect
	            if (resultCode == Activity.RESULT_OK) {
	                connectDevice(data, true);
	            }
	            break;
	        case BluetoothMessages.REQUEST_CONNECT_DEVICE_INSECURE:
	            // When DeviceListActivity returns with a device to connect
	            if (resultCode == Activity.RESULT_OK) {
	                connectDevice(data, false);
	            }
	            break;
	        case BluetoothMessages.REQUEST_ENABLE_BT:
	            // When the request to enable Bluetooth returns
	            if (resultCode == Activity.RESULT_OK) {
	                // Bluetooth is now enabled, so set up a chat session
	                setupChat();
	            } else {
	                // User did not enable Bluetooth or an error occured
	                Log.d(TAG, "BT not enabled");
	                Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
	                finish();
	            }
	        }
	    }

	    private void connectDevice(Intent data, boolean secure) {
	        // Get the device MAC address
	        String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
	        // Get the BLuetoothDevice object
	        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
	        // Attempt to connect to the device
	        mService.connect(device, secure);
	    }

	    @Override
	    public boolean onCreateOptionsMenu(Menu menu) {
	        MenuInflater inflater = getMenuInflater();
	        inflater.inflate(R.menu.option_menu, menu);
	        return true;
	    }

	    @Override
	    public boolean onOptionsItemSelected(MenuItem item) {
	        Intent serverIntent = null;
	        switch (item.getItemId()) {
	        case R.id.secure_connect_scan:
	            // Launch the DeviceListActivity to see devices and do scan
	            serverIntent = new Intent(this, DeviceListActivity.class);
	            startActivityForResult(serverIntent, BluetoothMessages.REQUEST_CONNECT_DEVICE_SECURE);
	            return true;
	        case R.id.insecure_connect_scan:
	            // Launch the DeviceListActivity to see devices and do scan
	            serverIntent = new Intent(this, DeviceListActivity.class);
	            startActivityForResult(serverIntent, BluetoothMessages.REQUEST_CONNECT_DEVICE_INSECURE);
	            return true;
	        case R.id.discoverable:
	            // Ensure this device is discoverable by others
	            ensureDiscoverable();
	            return true;
	        }
	        return false;
	    }

	    
	}